package cn.yunbamboos.rest.service.core;

import cn.hutool.core.util.ObjUtil;
import cn.yunbamboos.exception.AppException;
import cn.yunbamboos.rest.service.IInDTOConverter;
import cn.yunbamboos.rest.service.IFilterExecute;
import cn.yunbamboos.rest.service.IRestService;
import cn.yunbamboos.rest.service.IRestServiceInvoke;
import cn.yunbamboos.rest.service.ISession;
import cn.yunbamboos.rest.service.ISessionContainer;
import cn.yunbamboos.rest.service.auth.IAuthenticationListener;
import cn.yunbamboos.rest.service.auth.event.AuthenticationEvent;
import cn.yunbamboos.rest.service.dto.IInDTOValidator;
import cn.yunbamboos.rest.service.dto.in.BaseInDTO;
import cn.yunbamboos.rest.service.logs.LogMetadataThreadLocal;
import cn.yunbamboos.rest.service.transaction.ServiceTransactionManager;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.time.LocalDateTime;
import java.util.List;

public class RestServiceInvoke implements IRestServiceInvoke {

    private final IInDTOConverter converter;
    private final IFilterExecute filterExecute;
    private final ISessionContainer sessionContainer;
    private final List<IAuthenticationListener> authenticationListeners;
    private final ServiceTransactionManager serviceTransactionManager;
    private final IInDTOValidator validator;

    public RestServiceInvoke(IInDTOConverter converter, IFilterExecute filterExecute, ISessionContainer sessionContainer, List<IAuthenticationListener> authenticationListeners, ServiceTransactionManager serviceTransactionManager, IInDTOValidator validator) {
        this.converter = converter;
        this.filterExecute = filterExecute;
        this.sessionContainer = sessionContainer;
        this.authenticationListeners = authenticationListeners;
        this.serviceTransactionManager = serviceTransactionManager;
        this.validator = validator;
    }

    public Object invoke(IRestService service) throws Exception{
        // 记录服务执行时间
        StopWatch watch = new StopWatch();
        try {
            ISession session = sessionContainer.getSession();
            if(ObjUtil.isNull(session)){
                throw AppException.c("session is null");
            }
            LogMetadataThreadLocal.setStartTime(LocalDateTime.now());
            watch.start();
            // 在目标方法执行前可以获取注解属性
            LogMetadataThreadLocal.setServiceName(service.name());
            // 执行服务前置过滤器
            filterExecute.before(service);
            // 执行权限认证事件  并解析token
            if (service.authentication()) {
                auth();
            }
            // 读取请求内容 转换对应的 DTO
            BaseInDTO inDTO = converter.convert();
            // 验证DTO参数
            validator.validator(inDTO, service);
            // 手动开启事务
            serviceTransactionManager.begin(service);
            // 调用服务
            Object result = service.invoke().invoke(inDTO);
            watch.stop();
            LogMetadataThreadLocal.setTime(watch.getTotalTimeMillis());
            // 手动提交事务
            serviceTransactionManager.commit(service);
            filterExecute.after(service);
            return result;
        } catch (Exception e) {
            // 手动事务回滚
            serviceTransactionManager.rollback(service);
            LogMetadataThreadLocal.setTime(watch.getTotalTimeMillis());
            throw e;
        } finally {
            // 手动事务释放
            serviceTransactionManager.release(service);
        }
    }

    private void auth() {
        ISession session = sessionContainer.getSession();
        HttpServletRequest request = session.getRequest();
        AuthenticationEvent event = new AuthenticationEvent(session, request);
        for (IAuthenticationListener listener : this.authenticationListeners) {
            if (!listener.auth(event)) {
                throw AppException.c(401, "登陆认证失败，请重新登陆");
            }
        }
    }

}
